/**
 * Alipay.com Inc.
 * Copyright (c) 2004-2023 All Rights Reserved.
 */
package com.example.spider.proxy.core.distinct;

import java.util.BitSet;
import java.util.concurrent.ConcurrentHashMap;

/**
 *
 */
public class MemoryIpPortDistincter implements IpPortDistincter {

    /**
     * ip端、port、ip 后两段
     */
    private final ConcurrentHashMap<String, BitSet> ipDistinct = new ConcurrentHashMap<>(100);

    @Override
    public boolean add(String ip, int port) {
        return getOrAdd(ip, port, true);
    }

    @Override
    public boolean contains(String ip, int port) {
        return getOrAdd(ip, port, false);
    }

    /**
     * 操作
     * @param ip ip
     * @param port 端口
     * @param add 操作
     * @return
     *  add：之前有返回false，之前没有返回true，表示是否新增元素
     *  !add：返回是否存在
     */
    private boolean getOrAdd(String ip, int port, boolean add) {
        String[] ipParts = ip.split("\\.");
        String prefix = ipParts[0] + ensure3Digest(ipParts[1]);
        String key = prefix + "_" + port;
        int suffixToInt = Integer.parseInt(ipParts[2]) << 8 | Integer.parseInt(ensure3Digest(ipParts[3]));
        BitSet bs = ipDistinct.computeIfAbsent(key, k -> new BitSet(256 * 256));
        boolean exist = bs.get(suffixToInt);
        if (add) {
            if (exist) {
                return false;
            } else {
                bs.set(suffixToInt);
                return true;
            }
        } else {
            return exist;
        }
    }


    private static String ensure3Digest(String part) {
        int length = part.length();
        if (length == 3) {
            return part;
        }
        return "0".repeat(3 - length) + part;
    }

    /**
     * 将 ip 字符串转换为 int 类型的数字
     * <p>
     * 思路就是将 ip 的每一段数字转为 8 位二进制数，并将它们放在结果的适当位置上
     *
     * @param ipString ip字符串，如 127.0.0.1
     * @return ip字符串对应的 int 值
     */
    public static int ip2Int(String ipString) {
        // 取 ip 的各段
        String[] ipSlices = ipString.split("\\.");
        int rs = 0;
        for (int i = 0; i < ipSlices.length; i++) {
            // 将 ip 的每一段解析为 int，并根据位置左移 8 位
            int intSlice = Integer.parseInt(ipSlices[i]) << 8 * i;
            // 或运算
            rs = rs | intSlice;
        }
        return rs;
    }

    /**
     * 将 int 转换为 ip 字符串
     *
     * @param ipInt 用 int 表示的 ip 值
     * @return ip字符串，如 127.0.0.1
     */
    public static String int2Ip(int ipInt) {
        String[] ipString = new String[4];
        for (int i = 0; i < 4; i++) {
            // 每 8 位为一段，这里取当前要处理的最高位的位置
            int pos = i * 8;
            // 取当前处理的 ip 段的值
            int and = ipInt & (255 << pos);
            // 将当前 ip 段转换为 0 ~ 255 的数字，注意这里必须使用无符号右移
            ipString[i] = String.valueOf(and >>> pos);
        }
        return String.join(".", ipString);
    }

}